package editor.widgets;

import editor.PwnIDE;
import event.StringChangeEvent;
import event.UndoSet;
import event.Undoable;
import observer.ObservedString;
import observer.StringObserver;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.AncestorListener;
import javax.swing.event.AncestorEvent;
import java.awt.*;

/**
 * This is a {@link JTextField} that automatically updates the {@link ObservedString} that it manages,
 * so that the model is updated properly.
 *
 * @author Neil Dickson
 */
public class ObservedStringField extends JTextField implements StringObserver, DocumentListener, AncestorListener {
	private ObservedString	value;

	private	boolean			isUpdating;
	private boolean			isAutoResizing;

	private int				rowHeight;

	public ObservedStringField(ObservedString value,int columns) {
		this(value,columns,false);
	}
	public ObservedStringField(ObservedString value,int columns,boolean autoResizing) {
		super(Math.max(1,columns));
		this.value = value;
		isUpdating = false;
		isAutoResizing = autoResizing;
		rowHeight = -1;
		setText(value.toString(),false);
		value.addObserver(this);
		getDocument().addDocumentListener(this);
		addAncestorListener(this);
	}

	public void valueChanged(ObservedString s, String oldValue) {
		setText(s.toString(),false);
	}

	public void setText(String text,boolean updateString) {
		isUpdating = true;
		super.setText(text);
		isUpdating = false;
		if (updateString) {
			handleCulpritTextChange();
		}
	}
	public void setText(String text) {
		setText(text,true);
	}

	public void setBackground(Color bg) {
		super.setBackground(bg);
		setOpaque(bg.getAlpha()==255);
	}

	public int getTextWidth() {
		return getFontMetrics(getFont()).stringWidth(getText());
	}
	public int getTextWidthInColumns() {
		// Round up by adding one less than the column width before dividing by column width
		return (getFontMetrics(getFont()).stringWidth(getText())+getColumnWidth()-1)/getColumnWidth();
	}

	public void insertUpdate(DocumentEvent e) {
		if (!isUpdating) {
			handleCulpritTextChange();
		}
	}

	public void removeUpdate(DocumentEvent e) {
		if (!isUpdating) {
			handleCulpritTextChange();
		}
	}

	public void changedUpdate(DocumentEvent e) {
		if (!isUpdating) {
			handleCulpritTextChange();
		}
	}

	public int getRowHeight() {
		if (rowHeight==-1) {
			rowHeight = getFontMetrics(getFont()).getHeight();
			return rowHeight;
		}
		return rowHeight;
	}

	public boolean isAutoResizing() {
		return isAutoResizing;
	}

	public void setAutoResizing(boolean autoResizing) {
		isAutoResizing = autoResizing;
		if (isAutoResizing) {
			setColumns(Math.max(1,getTextWidthInColumns()));
		}
	}

	private void handleCulpritTextChange() {
		if (!value.toString().equals(getText())) {
			if (isAutoResizing) {
				setColumns(Math.max(1,getTextWidthInColumns()));
			}
			String oldValue = value.toString();
			value.setValue(this,getText());
			StringChangeEvent event = new StringChangeEvent(value,oldValue);
			Undoable lastEvent = PwnIDE.editor.getUndoTop();
			if ((lastEvent!=null && lastEvent instanceof StringChangeEvent && ((StringChangeEvent)lastEvent).getString()==value) ||
					(lastEvent!=null && lastEvent instanceof UndoSet && ((UndoSet)lastEvent).get(0) instanceof StringChangeEvent && ((StringChangeEvent)((UndoSet)lastEvent).get(0)).getString()==value)) {
				PwnIDE.editor.mergeWithUndoTop(event);
			}
			else {
				PwnIDE.editor.eventDone(event);
			}
		}
	}

	public void ancestorAdded(AncestorEvent event) {
		value.addObserver(this);
	}

	public void ancestorRemoved(AncestorEvent event) {
		value.removeObserver(this);
	}

	public void ancestorMoved(AncestorEvent event) {}
}
